package com.wdl.modules.myhouse.security.config;

import com.wdl.modules.myhouse.dao.security.SecurityDao;

import com.wdl.modules.myhouse.exception.TokenException;
import com.wdl.modules.myhouse.exception.VerifiCodeException;
import com.wdl.modules.myhouse.security.entity.UserInfo;

import com.wdl.modules.myhouse.security.filter.MailLoginFilter;
import com.wdl.modules.myhouse.security.filter.ResponseFilter;
import com.wdl.modules.myhouse.security.filter.TokenFilter;

import com.wdl.modules.myhouse.service.SysUserService;
import com.wdl.modules.myhouse.service.UserService;
import com.wdl.modules.myhouse.common.Code;
import com.wdl.modules.myhouse.common.Result;
import com.wdl.modules.myhouse.utils.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.*;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import java.io.IOException;

import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

/**
 * @author 昏昏的君
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private SecurityDao securityDao;
    @Autowired
    private TokenFilter tokenFilter;
    @Autowired
    private MailLoginFilter mailLoginFilter;

    @Autowired
    private ResponseFilter responseFilter;
    @Autowired
    private UserService userService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private AuthenticationSuccessHandler successHandler;
//
//    @Autowired
//    private MailLoginSecurityConfig loginSecurityConfig;


    @Bean
    @Override
    @Order(4)
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors();
        http
                .exceptionHandling()
                .authenticationEntryPoint(enterPointException());
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http
                .formLogin()
                .successHandler(successHandler)
                .failureHandler(failHandler())

                .and()
                .exceptionHandling()
                .authenticationEntryPoint(enterPointException())
                .and()
                .csrf().disable();
        //remember-me
        http
                .rememberMe()
                .tokenRepository(persistentTokenRepository()) // 配置token持久化仓库
                .rememberMeCookieName("remember")
                .rememberMeParameter("remember")
                .tokenValiditySeconds(3600) // 过期时间，单位为秒
                .userDetailsService(userDetailsService()); // 处理自动登录逻辑
        http
                .authorizeRequests()
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).anonymous()
                .antMatchers(
                        "/test/**",
                        "/v2/**",
                        "/security/**",
                        "/profile/**",
                        "/common/download**",
                        "/common/download/resource**",
                        "/swagger-ui.html",
                        "/swagger-resources/**",
                        "/webjars/**",
                        "/*/api-docs",
                        "/druid/**"
                )
                .permitAll()
                .anyRequest()
                .authenticated();
        //过滤器
        http
                //response-->token-->mailLogin
                .addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(responseFilter, TokenFilter.class)
                .addFilterAfter(mailLoginFilter, TokenFilter.class)
        ;

    }
    @Bean
    public AuthenticationEntryPoint enterPointException() {
        return new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                Object exception1 = request.getAttribute("NativeException");
                if (exception1 != null) {
                    AuthenticationException exception = (AuthenticationException) exception1;
                    if (exception instanceof VerifiCodeException) {
                        //验证码异常
                        Result<String> res = new Result<String>().error(Code.VERIFY_CODE_ERROR.getCode(), exception.getMessage());
                        ResponseUtil.printObj(res, response);
                    } else {
                        if (authException instanceof InsufficientAuthenticationException) {
                            // 匿名或者remember访问出异常来这
                            Result<Object> res = new Result<>().error(Code.REMEMBER_COOKIE_NOT_COORECT.getCode(), Code.REMEMBER_COOKIE_NOT_COORECT.getMessage());
                            response.getWriter().println(JsonUtil.obj2String(res));
                        } else {
                            log.warn("其他异常发生了:" + authException.getMessage());
                        }
                    }
                }
            }
        };
    }
    /**
     * token持久化
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
        repository.setDataSource(dataSource);
        return repository;
    }
    @Bean
    public AuthenticationProvider authenticationProvider() {
        //对默认的UserDetailsService进行覆盖
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        // 默认的provider 所支持的是 usernamePasswordToken
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        authenticationProvider.setHideUserNotFoundExceptions(false);
        return authenticationProvider;
    }
    @Bean
    @Order(1)
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Bean
    @Order(2)
    public UserDetailsService userDetailsService() {
        return username -> {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication == null) {
                UserInfo userInfo = securityDao.getUserInfoByUsername(username);
                if (userInfo == null) {
                    throw new UsernameNotFoundException("用户不存在");
                }
                return userInfo;
            } else {
                return (UserInfo) authentication.getPrincipal();
            }
        };
    }
    @Bean
    public AuthenticationFailureHandler failHandler() {
        return new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                Result res = null;

                Object exception1 = request.getAttribute("LOGINException");
                if (exception1 != null) {
                    AuthenticationException mailexception = (AuthenticationException) exception1;
                    if (exception instanceof VerifiCodeException) {
                        //验证码异常
                        res = new Result<String>().error(Code.VERIFY_CODE_ERROR.getCode(), exception.getMessage());
                        ResponseUtil.printObj(res, response);
                    }else if(exception instanceof TokenException){
                        res = new Result<String>().error(Code.TOKEN_UNLEGAL.getCode(), exception.getMessage());
                        ResponseUtil.printObj(res, response);
                    }
                }else{
                    if (exception instanceof UsernameNotFoundException) {
                        // 用户不存在
                        res = new Result().error(Code.USER_NOT_FOUND.getCode(), Code.USER_NOT_FOUND.getMessage());
                    } else if (exception instanceof BadCredentialsException) {
                        res = new Result().error(Code.PASSWORD_NOT_CORRECT.getCode(), Code.PASSWORD_NOT_CORRECT.getMessage());
                    } else if (exception instanceof InternalAuthenticationServiceException) {
                        res = new Result().error(Code.USER_NOT_FOUND.getCode(), "找不到对应的用户信息");
                    } else if (exception instanceof InvalidCookieException) {
                        res = new Result().error(Code.REMEMBER_COOKIE_NOT_COORECT.getCode(), Code.REMEMBER_COOKIE_NOT_COORECT.getMessage());
                    } else {
                        res = new Result().error();
                    }
                }
                response.getWriter().println(JsonUtil.obj2String(res));
            }
        };
    }
}

